//
// Created by guoxingyu on 24-9-26.
//
#include "evaluator.h"
#include <stdexcept>
using namespace std;
/*
 * @brief 构造函数
 * @param params 加密参数上下文
 * @param evaluationKey 评估密钥
 */
Evaluator::Evaluator(Context *params, EvaluationKey *evaluationKey)
    : params(params), ringQ(params->ringQ), ringP(params->ringP), ringQMul(params->ringQMul)
{
    t = params->T();
    pHalf = new mpz_class(*(ringQMul->modulusBigint) >> 1);

    poolQ.resize(4);
    poolQMul.resize(4);
    for (int i = 0; i < 4; i++) {
        poolQ[i].resize(6);
        poolQMul[i].resize(6);
        for (int j = 0; j < 6; j++) {
            poolQ[i][j] = ringQ->NewPoly();
            poolQMul[i][j] = ringQMul->NewPoly();
        }
    }
    tmpPt = new Plaintext(params, params->MaxLevel());

    lightEncoder = new Encoder(params);
    baseconverterQ1Q2 = new FastBasisExtender(ringQ, ringQMul);
    baseconverterQ2Q1 = new FastBasisExtender(ringQMul, ringQ);
    if (params->PCount() != 0) {
        keySwitcher = new KeySwitcher(params);
    }
    rlk = evaluationKey->rlk;
    rtks = evaluationKey->rtks;
}

Evaluator::Evaluator(Context *params)
    : params(params), ringQ(params->ringQ), ringP(params->ringP), ringQMul(params->ringQMul)
{
    t = params->T();
    pHalf = new mpz_class(*(ringQMul->modulusBigint) >> 1);

    poolQ.resize(4);
    poolQMul.resize(4);
    for (int i = 0; i < 4; i++) {
        poolQ[i].resize(6);
        poolQMul[i].resize(6);
        for (int j = 0; j < 6; j++) {
            poolQ[i][j] = ringQ->NewPoly();
            poolQMul[i][j] = ringQMul->NewPoly();
        }
    }
    tmpPt = new Plaintext(params, params->MaxLevel());
    lightEncoder = new Encoder(params);
    baseconverterQ1Q2 = new FastBasisExtender(ringQ, ringQMul);
    baseconverterQ2Q1 = new FastBasisExtender(ringQMul, ringQ);
    if (params->PCount() != 0) {
        keySwitcher = new KeySwitcher(params);
    }
    rlk = nullptr;
    rtks = nullptr;
}
Evaluator::~Evaluator()
{
    delete ringQ;
    delete ringP;
    delete ringQMul;
    delete baseconverterQ1Q2;
    delete tmpPt;

    for (auto &row : poolQ) {
        for (auto poly : row) {
            delete poly;
        }
    }

    for (auto &row : poolQMul) {
        for (auto poly : row) {
            delete poly;
        }
    }
}

std::tuple<Ciphertext *, Ciphertext *> GetElemAndCheckUnary(Operand *op0, Operand *opOut, int opOutMinDegree)
{
    if (op0 == nullptr || opOut == nullptr) {
        throw std::runtime_error("operand cannot be nil");
    }

    if (op0->Degree() == 0) {
        throw std::runtime_error("operand cannot be plaintext");
    }

    if (opOut->Degree() < opOutMinDegree) {
        throw std::runtime_error("receiver operand degree is too small");
    }
    return { op0->El(), opOut->El() };
}
/*
 * @brief 检查二元运算的操作数并获取相应的密文元素
 *
 * @param op0 第一个操作数
 * @param op1 第二个操作数
 * @param opOut 输出操作数
 * @param opOutMinDegree 输出操作数所需的最小次数
 * @param ensureRingQ 是否确保操作数在环 Rq 中
 *
 * @return std::tuple<Ciphertext*, Ciphertext*, Ciphertext*> 返回一个元组，包含三个密文指针:
 * - 第一个操作数对应的密文
 * - 第二个操作数对应的密文
 * - 输出操作数对应的密文
 */
std::tuple<Ciphertext *, Ciphertext *, Ciphertext *> Evaluator::GetElemAndCheckBinary(Operand *op0, Operand *op1,
    Operand *opOut, uint64_t opOutMinDegree, bool ensureRingQ)
{
    if (op0 == nullptr || op1 == nullptr || opOut == nullptr) {
        throw std::invalid_argument("operands cannot be null");
    }

    if (op0->Degree() + op1->Degree() == 0) {
        throw std::invalid_argument("operands cannot be both plaintexts");
    }

    if (opOut->Degree() < opOutMinDegree) {
        throw std::invalid_argument("receiver operand degree is too small");
    }

    if (ensureRingQ) {
        return { GetRingQElem(op0), GetRingQElem(op1), opOut->El() }; // lifts from Rt to Rq if necessary
    }

    return { op0->El(), op1->El(), opOut->El() };
}
/*
 * @brief 将密文从环 Rt 提升到环 Rq 并进行 NTT 变换
 * @param ct 输入密文
 * @param cQ 在环 Rq 中的多项式向量
 * @param cQMul 在环 RqMul 中的多项式向量
 *
 * 该函数执行两个主要操作:
 * 1. 使用基变换将密文从环 Rt 提升到环 Rq
 * 2. 对提升后的密文进行 NTT(数论变换)以便后续运算
 */
void Evaluator::ModUpAndNTT(Ciphertext *ct, std::vector<Poly *> &cQ, std::vector<Poly *> &cQMul)
{
    uint64_t levelQ = ringQ->Modulus.size() - 1;
    for (size_t i = 0; i < ct->value.size(); i++) {
        baseconverterQ2Q1->BaseConvertP2Q(ringQMul->Modulus.size() - 1, levelQ, ct->value[i], cQMul[i]);
        ringQ->NTT(ct->value[i], cQ[i]);
        ringQMul->NTT(cQMul[i], cQMul[i]);
    }
}

/*
 * @brief 将密文从环 Rt 提升到环 Rq 并进行 NTT 变换
 *
 * @param ct 输入密文
 * @param cQ 在环 Rq 中的多项式向量
 * @param cQMul 在环 RqMul 中的多项式向量
 *
 * 该函数执行两个主要操作:
 * 1. 使用基变换将密文从环 Rt 提升到环 Rq
 * 2. 对提升后的密文进行 NTT(数论变换)以便后续运算
 */
void Evaluator::TensoreLowDeg(Ciphertext *ct0, Ciphertext *ct1)
{
    // Retrieve polynomial pools
    std::vector<Poly *> c0Q1 = poolQ[0];
    std::vector<Poly *> c1Q1 = poolQ[1];
    std::vector<Poly *> c2Q1 = poolQ[2];

    std::vector<Poly *> c0Q2 = poolQMul[0];
    std::vector<Poly *> c1Q2 = poolQMul[1];
    std::vector<Poly *> c2Q2 = poolQMul[2];

    // Case when ct0 is the same as ct1 (self-tensor operation)
    if (ct0 == ct1) {
        // Perform tensor operation for lower degrees
        ringQ->MulCoeffsBarrett(c0Q1[0], c0Q1[0], c2Q1[0]);
        ringQMul->MulCoeffsBarrett(c0Q2[0], c0Q2[0], c2Q2[0]);

        ringQ->MulCoeffsBarrett(c0Q1[0], c0Q1[1], c2Q1[1]);
        ringQMul->MulCoeffsBarrett(c0Q2[0], c0Q2[1], c2Q2[1]);

        ringQ->AddNoMod(c2Q1[1], c2Q1[1], c2Q1[1]);
        ringQMul->AddNoMod(c2Q2[1], c2Q2[1], c2Q2[1]);

        ringQ->MulCoeffsBarrett(c0Q1[1], c0Q1[1], c2Q1[2]);
        ringQMul->MulCoeffsBarrett(c0Q2[1], c0Q2[1], c2Q2[2]);
    } else { // Case when ct0 and ct1 are different
        ringQ->MulCoeffsBarrett(c0Q1[0], c1Q1[0], c2Q1[0]);
        ringQMul->MulCoeffsBarrett(c0Q2[0], c1Q2[0], c2Q2[0]);
        ringQ->MulCoeffsBarrett(c0Q1[0], c1Q1[1], c2Q1[1]);
        ringQMul->MulCoeffsBarrett(c0Q2[0], c1Q2[1], c2Q2[1]);

        ringQ->MulCoeffsBarrettAndAddNoMod(c0Q1[1], c1Q1[0], c2Q1[1]);
        ringQMul->MulCoeffsBarrettAndAddNoMod(c0Q2[1], c1Q2[0], c2Q2[1]);

        ringQ->MulCoeffsBarrett(c0Q1[1], c1Q1[1], c2Q1[2]);
        ringQMul->MulCoeffsBarrett(c0Q2[1], c1Q2[1], c2Q2[2]);
    }
}
/*
 * @brief 执行密文张量乘法运算的低次项部分
 * @param ct0 第一个输入密文
 * @param ct1 第二个输入密文
 *
 * 该函数计算两个密文张量乘法的低次项部分:
 * - 如果两个输入密文相同(自张量),执行优化的计算
 * - 如果两个输入密文不同,执行标准的张量乘法
 *
 * 计算在两个环中同时进行:
 * - ringQ: 主要密文空间
 * - ringQMul: 乘法专用空间
 */
void Evaluator::TensortLargeDeg(Ciphertext *ct0, Ciphertext *ct1)
{
    std::vector<Poly *> c0Q1 = poolQ[0];
    std::vector<Poly *> c1Q1 = poolQ[1];
    std::vector<Poly *> c2Q1 = poolQ[2];

    std::vector<Poly *> c0Q2 = poolQMul[0];
    std::vector<Poly *> c1Q2 = poolQMul[1];
    std::vector<Poly *> c2Q2 = poolQMul[2];

    for (size_t i = 0; i < ct0->Degree() + ct1->Degree() + 1; i++) {
        c2Q1[i]->Zero();
        c2Q2[i]->Zero();
    }
    for (size_t i = 0; i < ct0->value.size(); i++) {
        for (size_t j = 0; j < ct1->value.size(); j++) {
            ringQ->MulCoeffsBarrettAndAdd(c0Q1[i], c1Q1[j], c2Q1[i + j]);
            ringQMul->MulCoeffsBarrettAndAdd(c0Q2[i], c1Q2[j], c2Q2[i + j]);
        }
    }
}
/*
 * @brief 对密文进行量化操作
 * 具体步骤如下:
 * - 对每个密文分量执行:
 * 1. 在两个环中进行逆 NTT 变换
 * 2. 执行 Q2->Q1 的基变换
 * 3. 添加 pHalf 进行舍入
 * 4. 执行 Q1->Q2 的基变换
 * 5. 减去 pHalf
 * 6. 乘以明文模数 t
 * @param ctOut 需要量化的密文
 *
 */
void Evaluator::Quantize(Ciphertext *ctOut)
{
    uint64_t levelQ = ringQ->Modulus.size() - 1;
    uint64_t levelQMul = ringQMul->Modulus.size() - 1;

    std::vector<Poly *> c2Q1 = poolQ[2];
    std::vector<Poly *> c2Q2 = poolQMul[2];

    for (size_t i = 0; i < ctOut->value.size(); i++) {
        ringQ->InvNTTLvl(c2Q1[i]->Level(), c2Q1[i], c2Q1[i]);
        ringQMul->InvNTTLvl(c2Q2[i]->Level(), c2Q2[i], c2Q2[i]);
        baseconverterQ2Q1->BaseConvertQP2Q(levelQMul, levelQ, c2Q2[i], c2Q1[i], c2Q2[i]);
        ringQMul->AddScalarBigint(c2Q2[i], *pHalf, c2Q2[i]);
        baseconverterQ1Q2->BaseConvertP2Q(levelQ, levelQMul, c2Q2[i], ctOut->value[i]);
        ringQ->SubScalarBigint(ctOut->value[i], *pHalf, ctOut->value[i]);
        ringQ->MulScalarBarrett(ctOut->value[i], t, ctOut->value[i]);
    }
}
/*
 * @brief 执行密文乘法运算
 * @param op0 第一个操作数
 * @param op1 第二个操作数
 * @param ctOut 输出密文
 * @param type 乘法类型
 */
void Evaluator::Mul(Ciphertext *op0, Operand *op1, Ciphertext *ctOut, MultiType type)
{
    auto [el0, el1, elOut] = GetElemAndCheckBinary(op0, op1, ctOut, op0->Degree() + op1->Degree(), false);
    switch (type) {
        case MultiType::plaintextMul:
            MulPlaintextMul(op0, dynamic_cast<PlaintextMul *>(op1), ctOut);
            break;
        case MultiType::plaintextRingT:
            MulPlaintextRingT(op0, dynamic_cast<PlaintextRingT *>(op1), ctOut);
            break;
        case MultiType::ciphertext:
        case MultiType::plaintext:
            TensorAndRescale(el0, el1, elOut);
            break;
        default:
            exit(-1);
    }
}

/*
 * @brief 执行密文张量乘法运算并进行量化操作
 * @param ct0 第一个输入密文
 * @param ct1 第二个输入密文
 * @param ctOut 输出密文
 *
 * 该函数执行以下步骤:
 * - 对输入密文进行张量乘法运算
 * - 对结果进行量化操作
 *
 * 具体实现:
 * - 在两个环中进行张量乘法运算
 * - 对结果进行量化操作
 */
void Evaluator::TensorAndRescale(Ciphertext *ct0, Ciphertext *ct1, Ciphertext *ctOut)
{
    std::vector<Poly *> c0Q1 = poolQ[0];
    std::vector<Poly *> c0Q2 = poolQMul[0];
    std::vector<Poly *> c1Q1 = poolQ[1];
    std::vector<Poly *> c1Q2 = poolQMul[1];

    ModUpAndNTT(ct0, c0Q1, c0Q2);
    if (ct0 != ct1) {
        ModUpAndNTT(ct1, c1Q1, c1Q2);
    }

    if (ct0->Degree() == 1 && ct1->Degree() == 1) {
        TensoreLowDeg(ct0, ct1);
    } else if ((ct0->Degree() == 0 && ct1->Degree() == 1) || (ct0->Degree() == 1 && ct1->Degree() == 0)) {
        TensortLargeDeg(ct0, ct1);
    } else {
        throw std::runtime_error("Mult failed: Cipertext Degree do not match");
    }

    Quantize(ctOut);
}
/*
 * @brief 执行密文与明文乘法运算
 * @param ct0 输入密文
 * @param ptRt 输入明文
 * @param ctOut 输出密文
 *
 * 该函数执行以下步骤:
 * - 对输入密文进行 NTT 变换
 * - 对输入明文进行乘法运算
 * - 对结果进行逆 NTT 变换
 */
void Evaluator::MulPlaintextMul(Ciphertext *ct0, PlaintextMul *ptRt, Ciphertext *ctOut) const
{
    for (auto i = 0; i < ct0->value.size(); i++) {
        ringQ->NTT(ct0->value[i], ctOut->value[i]);
        ringQ->MulCoeffsBarrettConstant(ctOut->value[i], ptRt->value, ctOut->value[i]);
        ringQ->InvNTT(ctOut->value[i], ctOut->value[i]);
    }
}
/*
 * @brief 执行密文与明文乘法运算
 * @param ct0 输入密文
 * @param ptRt 输入RingT上的明文
 * @param ctOut 输出密文
 *
 * 该函数执行以下步骤:
 * - 对输入密文进行 NTT 变换
 * - 对输入明文进行乘法运算
 * - 对结果进行逆 NTT 变换
 */
void Evaluator::MulPlaintextRingT(Ciphertext *ct0, PlaintextRingT *ptRt, Ciphertext *ctOut)
{
#pragma omp parallel for
    for (auto j = 0; j < ringQ->Modulus.size(); j++) {
        std::copy(ptRt->value->Coeffs[0].begin(), ptRt->value->Coeffs[0].end(), poolQ[0][0]->Coeffs[j].begin());
        ringQ->iNTTAndEqual(poolQ[0][0]->Coeffs[j], j);
    }
    for (auto i = 0; i < ct0->value.size(); i++) {
        ringQ->NTT(ct0->value[i], ctOut->value[i]);
        for (auto j = 0; j < ringQ->Modulus.size(); j++) {
            uint64_t qi = ringQ->Modulus[j];
            std::vector<uint64_t> bredParams = ringQ->bredParams[j];
            #pragma omp parallel for
            for (auto k = 0; k < ringQ->N; k++) {
                MulModBarrett(ctOut->value[i]->Coeffs[j][k], ctOut->value[i]->Coeffs[j][k], poolQ[0][0]->Coeffs[j][k],
                    qi, bredParams[2], bredParams[0], bredParams[1]);
            }
        }
        ringQ->InvNTT(ctOut->value[i], ctOut->value[i]);
    }
}
/*
 * @brief 设置评估密钥
 * @param evaluationKey 评估密钥
 *
 * 该函数设置评估密钥,用于后续的同态操作
 */
void Evaluator::WithKey(EvaluationKey *evaluationKey)
{
    rlk = evaluationKey->rlk;
    rtks = evaluationKey->rtks;
}
/*
 * @brief 执行同态行旋转操作
 * @param ct0 输入密文
 * @param ctOut 输出密文
 *
 * 该函数执行行旋转操作,用于同态加密中的行旋转操作
 */
void Evaluator::RotateRows(Ciphertext *ct0, Ciphertext *ctOut)
{
    if (ct0->Degree() != 1 || ctOut->Degree() != 1) {
        throw std::runtime_error("cannot RotateRows: input and/or output must be of degree 1");
    }

    uint64_t galEl = params->GaloisElementForRowRotation();

    SwitchingKey *key = rtks->GetRotationKey(galEl);
    if (key != nullptr) {
        Permute(ct0, galEl, key, ctOut);
    } else {
        throw std::runtime_error("evaluator has no rotation key for row rotation");
    }
}

/*
 * @brief 创建新的密文并执行行旋转操作
 * @param ct0 输入密文
 * @return 返回行旋转后的新密文
 *
 * 该函数创建一个新的密文对象,并在其上执行行旋转操作。
 * 它首先分配一个新的密文,然后调用RotateRows函数执行实际的旋转操作,
 * 最后返回旋转后的新密文。
 */
Ciphertext *Evaluator::RotateRowsNew(Ciphertext *ct0)
{
    auto *ctOut = new Ciphertext(params, 1, params->MaxLevel());
    RotateRows(ct0, ctOut);
    return ctOut;
}
/*
 * @brief 执行galois自同构操作
 * @param ct0 输入密文
 * @param generator 行旋转因子
 * @param switchKey 切换密钥
 * @param ctOut 输出密文
 *
 * 该函数执行galois自同构操作,用于同态加密中的galois自同构操作
 */
void Evaluator::ApplyGalois(Ciphertext *ct0, uint64_t generator, SwitchingKey *switchKey, Ciphertext *ctOut) const
{
    keySwitcher->SwitchKeysInPlace(ct0->value[1]->Level(), ct0->value[1], switchKey, keySwitcher->pool[1]->Q,
        keySwitcher->pool[2]->Q);
    ringQ->Add(keySwitcher->pool[1]->Q, ct0->value[0], keySwitcher->pool[1]->Q);
    ringQ->Permute(keySwitcher->pool[1]->Q, generator, ctOut->value[0]);
    ringQ->Permute(keySwitcher->pool[2]->Q, generator, ctOut->value[1]);
}

/* 自举编码后是右旋，对应BFV中的左旋 */
void Evaluator::ApplyGaloisInplace(Ciphertext *ct0, uint64_t generator, SwitchingKey *switchKey) const
{
    keySwitcher->SwitchKeysInPlace(ct0->value[1]->Level(), ct0->value[1], switchKey, keySwitcher->pool[1]->Q,
        keySwitcher->pool[2]->Q);
    ringQ->Add(keySwitcher->pool[1]->Q, ct0->value[0], keySwitcher->pool[1]->Q);
    ringQ->Permute(keySwitcher->pool[1]->Q, generator, ct0->value[0]);
    ringQ->Permute(keySwitcher->pool[2]->Q, generator, ct0->value[1]);
}

/* 原BFV中的左旋，在自举中对应apply_galois（结合自举编码后是右旋） */
void Evaluator::Permute(Ciphertext *ct0, uint64_t generator, SwitchingKey *switchKey, Ciphertext *ctOut) const
{
    keySwitcher->SwitchKeysInPlace(ct0->value[1]->Level(), ct0->value[1], switchKey, keySwitcher->pool[1]->Q,
        keySwitcher->pool[2]->Q);

    ringQ->Add(keySwitcher->pool[1]->Q, ct0->value[0], keySwitcher->pool[1]->Q);

    ringQ->Permute(keySwitcher->pool[1]->Q, generator, ctOut->value[0]);
    ringQ->Permute(keySwitcher->pool[2]->Q, generator, ctOut->value[1]);
}
/*
 * @brief 执行列旋转操作
 * @param ct0 输入密文
 * @param k 列旋转下标
 * @param ctOut 输出密文
 *
 * 该函数执行列旋转操作,用于同态加密中的列旋转操作
 */
void Evaluator::RotateColumns(Ciphertext *ct0, int k, Ciphertext *ctOut) const
{
    if (ct0->Degree() != 1 || ctOut->Degree() != 1) {
        throw std::runtime_error("cannot RotateColumns: input and or output must be of degree 1");
    }

    if (k == 0) {
        ctOut->Copy(ct0->El());
    } else {
        uint64_t galElL = params->GaloisElementForColumnRotationBy(k);
        // Looks in the rotation key if the corresponding rotation has been generated or if the input is a plaintext
        SwitchingKey *key = rtks->GetRotationKey(galElL);
        if (key != nullptr) {
            Permute(ct0, galElL, key, ctOut);
        } else {
            throw std::runtime_error(&"evaluator has no rotation key for rotation by "[k]);
        }
    }
}

Ciphertext *Evaluator::RotateColumnsNew(Ciphertext *ct0, int k)
{
    Ciphertext *ctOut = new Ciphertext(params, 1, params->MaxLevel());
    RotateColumns(ct0, k, ctOut);
    return ctOut;
}
/*
 * @brief 执行密钥切换操作
 * @param ct0 输入密文
 * @param switchKey 切换密钥
 * @param ctOut 输出密文
 *
 * 该函数执行密钥切换操作,用于同态加密中的密钥切换操作
 */
void Evaluator::SwitchKeys(Ciphertext *ct0, SwitchingKey *switchKey, Ciphertext *ctOut)
{
    if (ct0->Degree() != 1 || ctOut->Degree() != 1) {
        throw std::runtime_error("cannot SwitchKeys: input and output must be of degree 1 to allow key switching");
    }

    keySwitcher->SwitchKeysInPlace(ct0->value[1]->Level(), ct0->value[1], switchKey, keySwitcher->pool[1]->Q,
        keySwitcher->pool[2]->Q);

    ringQ->Add(ct0->value[0], keySwitcher->pool[1]->Q, ctOut->value[0]);
    CopyValues(keySwitcher->pool[2]->Q, ctOut->value[1]);
}
/*
 * @brief 创建新的密文并执行密钥切换操作
 * @param ct0 输入密文
 * @param switchkey 切换密钥
 * @return 返回密钥切换后的新密文
 *
 * 该函数创建一个新的密文对象,并在其上执行密钥切换操作。
 * 它首先分配一个新的密文,然后调用SwitchKeys函数执行实际的切换操作,
 * 最后返回切换后的新密文。
 */
Ciphertext *Evaluator::SwitchKeysNew(Ciphertext *ct0, SwitchingKey *switchkey)
{
    Ciphertext *ctOut = new Ciphertext(params, 1, params->MaxLevel());
    SwitchKeys(ct0, switchkey, ctOut);
    return ctOut;
}
/*
 * @brief 执行同态重线性化操作
 * @param ct0 输入密文
 * @param ctOut 输出密文
 *
 * 该函数执行同态重线性化操作,用于同态加密中的重线性化操作
 */
void Evaluator::Relinearize(Ciphertext *ct0, Ciphertext *ctOut)
{
    if (rlk == nullptr) {
        throw std::runtime_error("evaluator has no relinearization key");
    }

    if (ct0->Degree() - 1 > rlk->keys.size()) {
        throw std::runtime_error(
            "input ciphertext degree is too large to allow relinearization with the evluator's relinearization key");
    }

    if (ct0->Degree() < 2) {
        if (ct0 != ctOut) {
            ctOut->Copy(ct0->El());
        }
    } else {
        PrivateRelinearize(ct0, ctOut);
    }
}
/*
 * @brief 执行同态重线性化操作
 * @param ct0 输入密文
 * @param ctOut 输出密文
 *
 * 该函数执行同态重线性化操作,用于同态加密中的重线性化操作
 */
void Evaluator::PrivateRelinearize(Ciphertext *ct0, Ciphertext *ctOut)
{
    if (ctOut != ct0) {
        CopyValues(ct0->value[0], ctOut->value[0]); // -1-1
        CopyValues(ct0->value[1], ctOut->value[1]);
    }

    for (uint64_t deg = ct0->Degree(); deg > 1; deg--) {
        keySwitcher->SwitchKeysInPlace(ct0->value[deg]->Level(), ct0->value[deg], rlk->keys[deg - 2],
            keySwitcher->pool[1]->Q, keySwitcher->pool[2]->Q);
        ringQ->Add(ctOut->value[0], keySwitcher->pool[1]->Q, ctOut->value[0]);
        ringQ->Add(ctOut->value[1], keySwitcher->pool[2]->Q, ctOut->value[1]);
    }
    ctOut->value.resize(2);
}
/*
 * @brief 创建新的密文并执行重线性化操作
 * @param ct0 输入密文
 * @return 返回重线性化后的新密文
 *
 * 该函数创建一个新的密文对象,并在其上执行重线性化操作。
 * 它首先分配一个新的密文,然后调用Relinearize函数执行实际的重线性化操作,
 * 最后返回重线性化后的新密文。
 */
Ciphertext *Evaluator::RelinearizeNew(Ciphertext *ct0)
{
    Ciphertext *ctOut = new Ciphertext(params, 1, params->MaxLevel());
    Relinearize(ct0, ctOut);
    return ctOut;
}
/*
 * @brief 执行同态负操作
 * @param op 输入操作数
 * @param ctOut 输出密文
 *
 * 该函数执行同态负操作,用于同态加密中的负操作
 */
void Evaluator::Neg(Operand *op, Ciphertext *ctOut)
{
    auto [el0, elOut] = GetElemAndCheckUnary(op, ctOut, op->Degree());
    for (uint64_t i = 0; i < el0->value.size(); i++) {
        ringQ->Neg(el0->value[i], elOut->value[i]);
    }
}
/*
 * @brief 执行同态减法操作
 * @param op0 输入操作数
 * @param op1 输入操作数
 * @param ctOut 输出密文
 *
 * 该函数执行同态减法操作,用于同态加密中的减法操作
 */
void Evaluator::Sub(Operand *op0, Operand *op1, Ciphertext *ctOut)
{
    auto [el0, el1, elOut] = GetElemAndCheckBinary(op0, op1, ctOut, std::max(op0->Degree(), op1->Degree()), true);
    // evaluateInPlaceBinary(el0, el1, elOut, ringQ.Sub);
    auto [smallest, largest, flag] = GetSmallestLargest(el0, el1);

    for (uint64_t i = 0; i < smallest->Degree() + 1; i++) {
        ringQ->Sub(el0->value[i], el1->value[i], elOut->value[i]);
    }

    // If the inputs degrees differ, it copies the remaining degree on the receiver.
    if (largest != nullptr && largest != elOut) { // checks to avoid unnecessary work.
        for (uint64_t i = smallest->Degree() + 1; i < largest->Degree() + 1; i++) {
            elOut->value[i]->Copy(largest->value[i]);
        }
    }

    if (el0->Degree() < el1->Degree()) {
        for (int i = el0->Degree() + 1; i < el1->Degree() + 1; i++) {
            ringQ->Neg(ctOut->value[i], ctOut->value[i]);
        }
    }
}
/*
 * @brief 执行同态减法操作（不取模）
 * @param op0 输入操作数
 * @param op1 输入操作数
 * @param ctOut 输出密文
 *
 * 该函数执行同态减法操作（不取模）,用于同态加密中的减法操作
 */
void Evaluator::SubNoMod(Operand *op0, Operand *op1, Ciphertext *ctOut)
{
    auto [el0, el1, elOut] = GetElemAndCheckBinary(op0, op1, ctOut, std::max(op0->Degree(), op1->Degree()), true);

    // eval.evaluateInPlaceBinary(el0, el1, elOut, eval.ringQ.SubNoMod)
    auto [smallest, largest, flag] = GetSmallestLargest(el0, el1);

    for (uint64_t i = 0; i < smallest->Degree() + 1; i++) {
        ringQ->SubNoMod(el0->value[i], el1->value[i], elOut->value[i]);
    }

    // If the inputs degrees differ, it copies the remaining degree on the receiver.
    if (largest != nullptr && largest != elOut) { // checks to avoid unnecessary work.
        for (uint64_t i = smallest->Degree() + 1; i < largest->Degree() + 1; i++) {
            elOut->value[i]->Copy(largest->value[i]);
        }
    }

    if (el0->Degree() < el1->Degree()) {
        for (uint64_t i = el0->Degree() + 1; i < el1->Degree() + 1; i++) {
            ringQ->Neg(ctOut->value[i], ctOut->value[i]);
        }
    }
}
/*
 * @brief 执行同态取模操作
 * @param op 输入操作数
 * @param ctOut 输出密文
 *
 * 该函数执行同态取模操作,用于同态加密中的取模操作
 */
void Evaluator::Reduce(Operand *op, Ciphertext *ctOut)
{
    auto [el0, elOut] = GetElemAndCheckUnary(op, ctOut, op->Degree());
    for (uint64_t i = 0; i < el0->value.size(); i++) {
        ringQ->Reduce(el0->value[i], elOut->value[i]);
    }
}
/*
 * @brief 获取同态加密中的操作数
 * @param op 输入操作数
 * @return 返回操作数
 *
 * 该函数获取同态加密中的操作数,用于同态加密中的操作数获取
 */
Ciphertext *Evaluator::GetRingQElem(Operand *op)
{
    switch (op->type) {
        case MultiType::ciphertext:
        case MultiType::plaintext:
            return op->El();
        case MultiType::plaintextRingT:
            lightEncoder->ScaleUp(params->ringQ, params->ringT, keySwitcher->pool[0]->Q->Coeffs[0],
                static_cast<PlaintextRingT *>(op)->value, tmpPt->value);
            return tmpPt->El();
        default:
            throw std::runtime_error("invalid operand type for operation");
    }
}
/*
 * @brief 比较两个密文的次数并返回次数较小和较大的密文
 *
 * @param el0 第一个密文
 * @param el1 第二个密文
 * @return std::tuple<Ciphertext*, Ciphertext*, bool> 返回一个元组,包含:
 * - 次数较小的密文指针
 * - 次数较大的密文指针
 * - 两个密文次数是否相等的布尔值
 */
std::tuple<Ciphertext *, Ciphertext *, bool> Evaluator::GetSmallestLargest(Ciphertext *el0, Ciphertext *el1)
{
    if (el0->Degree() > el1->Degree()) {
        return { el1, el0, false };
    }
    if (el0->Degree() < el1->Degree()) {
        return { el0, el1, false };
    }
    return { el0, el1, true };
}

/*
 * @brief 执行密文与标量的乘法运算
 *
 * @param op 输入操作数
 * @param scalar 标量值
 * @param ctOut 输出密文
 *
 * 该函数执行以下步骤:
 * 1. 检查输入操作数并获取相应的密文元素
 * 2. 对密文的每个多项式系数与标量进行乘法运算
 * 3. 将结果存储在输出密文中
 */
void Evaluator::MulScalar(Operand *op, uint64_t scalar, Ciphertext *ctOut) const
{
    auto [el0, elOut] = GetElemAndCheckUnary(op, ctOut, op->Degree());
    for (size_t i = 0; i < el0->value.size(); i++) {
        ringQ->MulScalar(el0->value[i], scalar, elOut->value[i]);
    }
}
/*
 * @brief 执行两个密文的加法运算
 *
 * @param op0 第一个输入操作数
 * @param op1 第二个输入操作数
 * @param ctOut 输出密文
 *
 * 该函数执行同态加法运算:
 * 1. 检查输入操作数并获取相应的密文元素
 * 2. 使用EvaluateInPlaceBinary执行实际的加法运算
 * 3. 结果会被存储在ctOut中
 * 注意: 该操作会对结果进行模约化
 */
void Evaluator::Add(Operand *op0, Operand *op1, Ciphertext *ctOut)
{
    auto [el0, el1, elOut] = GetElemAndCheckBinary(op0, op1, ctOut, std::max(op0->Degree(), op1->Degree()), true);
    EvaluateInPlaceBinary(el0, el1, elOut, [this](Poly *a, Poly *b, Poly *c) { this->ringQ->Add(a, b, c); });
}
/*
 * @brief 创建新密文并执行加法运算
 *
 * @param op0 第一个输入密文
 * @param op1 第二个输入密文
 * @return Ciphertext* 返回一个新的包含加法结果的密文
 *
 * 该函数:
 * 1. 创建一个新的密文对象用于存储结果
 * 2. 调用Add函数执行加法运算
 * 3. 返回包含结果的新密文
 */
Ciphertext *Evaluator::AddNew(Ciphertext *op0, Ciphertext *op1)
{
    auto *ctOut = new Ciphertext(params, std::max(op0->Degree(), op1->Degree()), params->QCount() - 1);
    Add(op0, op1, ctOut);
    return ctOut;
}
/*
 * @brief 执行两个密文的加法运算(不进行模约化)
 *
 * @param op0 第一个输入操作数
 * @param op1 第二个输入操作数
 * @param ctOut 输出密文
 *
 * 该函数执行同态加法运算但不进行模约化:
 * 1. 检查输入操作数并获取相应的密文元素
 * 2. 使用EvaluateInPlaceBinary执行不带模约化的加法运算
 * 3. 结果会被存储在ctOut中
 * 注意: 该操作不会对结果进行模约化，可能导致系数增长
 */
void Evaluator::AddNoMod(Operand *op0, Operand *op1, Ciphertext *ctOut)
{
    auto [el0, el1, elOut] = GetElemAndCheckBinary(op0, op1, ctOut, std::max(op0->Degree(), op1->Degree()), true);
    EvaluateInPlaceBinary(el0, el1, elOut, [this](Poly *a, Poly *b, Poly *c) { this->ringQ->AddNoMod(a, b, c); });
}
/*
 * @brief 创建新密文并执行不带模约化的加法运算
 *
 * @param op0 第一个输入密文
 * @param op1 第二个输入密文
 * @return Ciphertext* 返回一个新的包含加法结果的密文
 *
 * 该函数:
 * 1. 创建一个新的密文对象用于存储结果
 * 2. 调用AddNoMod函数执行不带模约化的加法运算
 * 3. 返回包含结果的新密文
 */
Ciphertext *Evaluator::AddNoModNew(Ciphertext *op0, Ciphertext *op1)
{
    auto *ctOut = new Ciphertext(params, std::max(op0->Degree(), op1->Degree()), params->QCount() - 1);
    AddNoMod(op0, op1, ctOut);
    return ctOut;
}
/*
 * @brief 对两个密文执行通用的二元运算
 *
 * @param el0 第一个输入密文
 * @param el1 第二个输入密文
 * @param elOut 输出密文
 * @param evaluate 要执行的二元运算函数
 *
 * 该函数提供了一个通用框架来执行二元运算:
 * 1. 确定两个输入密文中次数较小和较大的一个
 * 2. 对较小次数范围内的所有项执行指定的二元运算
 * 3. 如果有较大次数的项，将其直接复制到输出密文中
 */
void Evaluator::EvaluateInPlaceBinary(Ciphertext *el0, Ciphertext *el1, Ciphertext *elOut,
    std::function<void(Poly *, Poly *, Poly *)> evaluate)
{
    
    auto [smallest, largest, _] = GetSmallestLargest(el0, el1);
    for (int i = 0; i <= smallest->Degree(); ++i) {
        evaluate(el0->value[i], el1->value[i], elOut->value[i]);
    }
    if (largest != nullptr && largest != elOut) {
        for (int i = smallest->Degree() + 1; i <= largest->Degree(); ++i) {
            elOut->value[i]->Copy(largest->value[i]);
        }
    }
}